package workers

import (
	"encoding/json"
	"gitee.com/captials-team/ubdframe/src/domain/dto/cmddata"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	v1log "gitee.com/captials-team/ubdframe/src/pkg/logs"
	"gitee.com/captials-team/ubdframe/src/pkg/wsserver"
	"go.uber.org/dig"
	"runtime/debug"
)

// WorkerMgr worker管理器
type WorkerMgr struct {
	di      *dig.Container
	l       v1log.ILog
	workers map[string]interfaces.IWebsocketWorker
}

func NewWorkerMgr(di *dig.Container, logger v1log.ILog) *WorkerMgr {
	return &WorkerMgr{
		di:      di,
		l:       logger,
		workers: make(map[string]interfaces.IWebsocketWorker),
	}
}

// AddWorker 添加worker，name指定消息结构中的action对应名称都会发送到worker中
func (mgr *WorkerMgr) AddWorker(name string, worker interfaces.IWebsocketWorker) {
	mgr.workers[name] = worker
}

func (mgr *WorkerMgr) SetHub(h *wsserver.Hub) {

}

func (mgr *WorkerMgr) Receive(msg *wsserver.WebSocketMessage) {
	mgr.l.Debug("receive msg %d,%s", msg.MessageType, string(msg.Message))

	cmd := mgr.convertCmd(msg.Message)
	if cmd == nil {
		return
	}

	cmd.Client = msg.Client
	cmd.OrigMessage = msg
	mgr.handleCmd(cmd)
}

func (mgr *WorkerMgr) Pong(messageType int, message []byte) (bool, []byte) {
	cmd := mgr.convertCmd(message)
	if cmd.Action == cmddata.PingCmd.Action {
		return true, cmddata.PongMessage
	}
	return false, nil
}

func (mgr *WorkerMgr) Online(client *wsserver.Client) {
	client.Conn().SetReadLimit(2048 * 1000)
	for _, w := range mgr.workers {
		w.Online(client)
	}
}

func (mgr *WorkerMgr) Close(client *wsserver.Client) {
	for _, w := range mgr.workers {
		w.Close(client)
	}
}

// convertCmd 将消息转为Cmd
func (mgr *WorkerMgr) convertCmd(message []byte) *cmddata.ClientCmd {
	cmd := cmddata.ClientCmd{}
	err := json.Unmarshal(message, &cmd)
	if err != nil {
		v1log.Out.Error("message invalid: %s", message)
		return &cmd
	}
	return &cmd
}

// handleCmd 处理消息cmd
func (mgr *WorkerMgr) handleCmd(cmd *cmddata.ClientCmd) {
	defer func() {
		if err := recover(); err != nil {
			v1log.Out.Error("recover err:  %s", err)
			v1log.Out.Error("recover stack %s", debug.Stack())
			cmd.Client.SendMessage(cmddata.CmdErrFrame)
		}
	}()

	//find worker
	w, exist := mgr.workers[cmd.ParseAction(0)]
	if !exist {
		v1log.Out.Error("ws worker not exist: %s", cmd.Action)
		cmd.Client.SendMessage(cmddata.CmdErrFrame)
		return
	}
	//logger.Info("ws action: %s", cmd.Action)

	//accredit
	if w.NeedAccredit(cmd.Client) {
		v1log.Out.Error("client need accredit: %s", cmd.Client.ID())
		cmd.Client.SendMessage(cmddata.NotAuthFrame)
		return
	}
	w.Receive(cmd)
}
